/*
 * Copyright (c) 2013, marco.tamburelli@gmail.com
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met: 
 * 
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer. 
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution. 
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package org.bmi.gwt.mi.shared.util;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.bmi.gwt.mi.shared.communication.ptypes.ArrayParam;
import org.bmi.gwt.mi.shared.communication.ptypes.array.ArrayArrayWrapper;
import org.bmi.gwt.mi.shared.communication.ptypes.array.ArrayWrapper;
import org.bmi.gwt.mi.shared.communication.ptypes.array.BooleanArrayWrapper;
import org.bmi.gwt.mi.shared.communication.ptypes.array.ByteArrayWrapper;
import org.bmi.gwt.mi.shared.communication.ptypes.array.CharacterArrayWrapper;
import org.bmi.gwt.mi.shared.communication.ptypes.array.DateArrayWrapper;
import org.bmi.gwt.mi.shared.communication.ptypes.array.DoubleArrayWrapper;
import org.bmi.gwt.mi.shared.communication.ptypes.array.FloatArrayWrapper;
import org.bmi.gwt.mi.shared.communication.ptypes.array.IntegerArrayWrapper;
import org.bmi.gwt.mi.shared.communication.ptypes.array.LongArrayWrapper;
import org.bmi.gwt.mi.shared.communication.ptypes.array.ObjectArrayWrapper;
import org.bmi.gwt.mi.shared.communication.ptypes.array.ShortArrayWrapper;
import org.bmi.gwt.mi.shared.communication.ptypes.array.StringArrayWrapper;
import org.bmi.gwt.mi.shared.exceptions.InvalidArgumentException;

import com.google.gwt.user.client.rpc.IsSerializable;

/** This is a set of functions used to pack and unpack arrays to parameters */
public class ArrayUtils
{
	/**
	 * From an array creates an {@link ArrayList}.
	 * 
	 * @param components
	 * @return
	 */
	public static <T> ArrayList<T> asList(T[] components)
	{
		ArrayList<T> list = new ArrayList<T>(components.length);

		for (T c : components)
			list.add(c);

		return list;
	}

	public static ArrayList<Boolean> asList(boolean[] array)
	{
		ArrayList<Boolean> list = new ArrayList<Boolean>(array.length);

		for (int i = 0; i < array.length; i++)
			list.add(array[i]);

		return list;
	}

	public static ArrayList<Byte> asList(byte[] array)
	{
		ArrayList<Byte> list = new ArrayList<Byte>(array.length);

		for (int i = 0; i < array.length; i++)
			list.add(array[i]);

		return list;
	}

	public static ArrayList<Character> asList(char[] array)
	{
		ArrayList<Character> list = new ArrayList<Character>(array.length);

		for (int i = 0; i < array.length; i++)
			list.add(array[i]);

		return list;
	}

	public static ArrayList<Double> asList(double[] array)
	{
		ArrayList<Double> list = new ArrayList<Double>(array.length);

		for (int i = 0; i < array.length; i++)
			list.add(array[i]);

		return list;
	}

	public static ArrayList<Float> asList(float[] array)
	{
		ArrayList<Float> list = new ArrayList<Float>(array.length);

		for (int i = 0; i < array.length; i++)
			list.add(array[i]);

		return list;
	}

	public static ArrayList<Integer> asList(int[] array)
	{
		ArrayList<Integer> list = new ArrayList<Integer>(array.length);

		for (int i = 0; i < array.length; i++)
			list.add(array[i]);

		return list;
	}

	public static ArrayList<Long> asList(long[] array)
	{
		ArrayList<Long> list = new ArrayList<Long>(array.length);

		for (int i = 0; i < array.length; i++)
			list.add(array[i]);

		return list;
	}

	public static ArrayList<Short> asList(short[] array)
	{
		ArrayList<Short> list = new ArrayList<Short>(array.length);

		for (int i = 0; i < array.length; i++)
			list.add(array[i]);

		return list;
	}

	public static boolean[] asBooleanArray(List<Boolean> list)
	{
		boolean[] arr = new boolean[list.size()];

		int i = 0;
		for (Boolean elem : list)
			arr[i++] = elem;

		return arr;
	}

	public static byte[] asByteArray(List<Byte> list)
	{
		byte[] arr = new byte[list.size()];

		int i = 0;
		for (Byte elem : list)
			arr[i++] = elem;

		return arr;
	}

	public static char[] asCharArray(List<Character> list)
	{
		char[] arr = new char[list.size()];

		int i = 0;
		for (Character elem : list)
			arr[i++] = elem;

		return arr;
	}

	public static double[] asDoubleArray(List<Double> list)
	{
		double[] arr = new double[list.size()];

		int i = 0;
		for (Double elem : list)
			arr[i++] = elem;

		return arr;
	}

	public static float[] asFloatArray(List<Float> list)
	{
		float[] arr = new float[list.size()];

		int i = 0;
		for (Float elem : list)
			arr[i++] = elem;

		return arr;
	}

	public static int[] asIntArray(List<Integer> list)
	{
		int[] arr = new int[list.size()];

		int i = 0;
		for (Integer elem : list)
			arr[i++] = elem;

		return arr;
	}

	public static long[] asLongArray(List<Long> list)
	{
		long[] arr = new long[list.size()];

		int i = 0;
		for (Long elem : list)
			arr[i++] = elem;

		return arr;
	}

	public static short[] asShortArray(List<Short> list)
	{
		short[] arr = new short[list.size()];

		int i = 0;
		for (Short elem : list)
			arr[i++] = elem;

		return arr;
	}

	public static Boolean[] asObject(boolean[] array)
	{
		Boolean[] arr = new Boolean[array.length];

		for (int i = 0; i < array.length; i++)
			arr[i] = array[i];

		return arr;
	}

	public static Byte[] asObject(byte[] array)
	{
		Byte[] arr = new Byte[array.length];

		for (int i = 0; i < array.length; i++)
			arr[i] = array[i];

		return arr;
	}

	public static Character[] asObject(char[] array)
	{
		Character[] arr = new Character[array.length];

		for (int i = 0; i < array.length; i++)
			arr[i] = array[i];

		return arr;
	}

	public static Double[] asObject(double[] array)
	{
		Double[] arr = new Double[array.length];

		for (int i = 0; i < array.length; i++)
			arr[i] = array[i];

		return arr;
	}

	public static Float[] asObject(float[] array)
	{
		Float[] arr = new Float[array.length];

		for (int i = 0; i < array.length; i++)
			arr[i] = array[i];

		return arr;
	}

	public static Integer[] asObject(int[] array)
	{
		Integer[] arr = new Integer[array.length];

		for (int i = 0; i < array.length; i++)
			arr[i] = array[i];

		return arr;
	}

	public static Long[] asObject(long[] array)
	{
		Long[] arr = new Long[array.length];

		for (int i = 0; i < array.length; i++)
			arr[i] = array[i];

		return arr;
	}

	public static Short[] asObject(short[] array)
	{
		Short[] arr = new Short[array.length];

		for (int i = 0; i < array.length; i++)
			arr[i] = array[i];

		return arr;
	}

	/**
	 * Convert an array to an object of {@link ArrayWrapper} type, to be later
	 * used inside an {@link ArrayParam} object.
	 * 
	 * @param array It should be an array.
	 * @return
	 * @throws InvalidArgumentException
	 */
	public static ArrayWrapper<?, ?> wrapArray(Object array) throws InvalidArgumentException
	{
		if (array instanceof boolean[])
		{
			BooleanArrayWrapper wrapper = new BooleanArrayWrapper();
			wrapper.setValues(asList((boolean[]) array));
	
			return wrapper;
		}
	
		if (array instanceof byte[])
		{
			ByteArrayWrapper wrapper = new ByteArrayWrapper();
			wrapper.setValues(asList((byte[]) array));
	
			return wrapper;
		}
	
		if (array instanceof char[])
		{
			CharacterArrayWrapper wrapper = new CharacterArrayWrapper();
			wrapper.setValues(asList((char[]) array));
	
			return wrapper;
		}
	
		if (array instanceof double[])
		{
			DoubleArrayWrapper wrapper = new DoubleArrayWrapper();
			wrapper.setValues(asList((double[]) array));
	
			return wrapper;
		}
	
		if (array instanceof float[])
		{
			FloatArrayWrapper wrapper = new FloatArrayWrapper();
			wrapper.setValues(asList((float[]) array));
	
			return wrapper;
		}
	
		if (array instanceof int[])
		{
			IntegerArrayWrapper wrapper = new IntegerArrayWrapper();
			wrapper.setValues(asList((int[]) array));
	
			return wrapper;
		}
	
		if (array instanceof long[])
		{
			LongArrayWrapper wrapper = new LongArrayWrapper();
			wrapper.setValues(asList((long[]) array));
	
			return wrapper;
		}
	
		if (array instanceof short[])
		{
			ShortArrayWrapper wrapper = new ShortArrayWrapper();
			wrapper.setValues(asList((short[]) array));
	
			return wrapper;
		}
	
		// --------------
	
		if (array instanceof Byte[])
		{
			ByteArrayWrapper wrapper = new ByteArrayWrapper();
			wrapper.setValues(asList((Byte[]) array));
	
			return wrapper;
		}
	
		if (array instanceof Character[])
		{
			CharacterArrayWrapper wrapper = new CharacterArrayWrapper();
			wrapper.setValues(asList((Character[]) array));
	
			return wrapper;
		}
	
		if (array instanceof Date[])
		{
			DateArrayWrapper wrapper = new DateArrayWrapper();
			wrapper.setValues(asList((Date[]) array));
	
			return wrapper;
		}
	
		if (array instanceof Double[])
		{
			DoubleArrayWrapper wrapper = new DoubleArrayWrapper();
			wrapper.setValues(asList((Double[]) array));
	
			return wrapper;
		}
	
		if (array instanceof Float[])
		{
			FloatArrayWrapper wrapper = new FloatArrayWrapper();
			wrapper.setValues(asList((Float[]) array));
	
			return wrapper;
		}
	
		if (array instanceof Integer[])
		{
			IntegerArrayWrapper wrapper = new IntegerArrayWrapper();
			wrapper.setValues(asList((Integer[]) array));
	
			return wrapper;
		}
	
		if (array instanceof Long[])
		{
			LongArrayWrapper wrapper = new LongArrayWrapper();
			wrapper.setValues(asList((Long[]) array));
	
			return wrapper;
		}
	
		if (array instanceof Short[])
		{
			ShortArrayWrapper wrapper = new ShortArrayWrapper();
			wrapper.setValues(asList((Short[]) array));
	
			return wrapper;
		}
	
		if (array instanceof String[])
		{
			StringArrayWrapper wrapper = new StringArrayWrapper();
			wrapper.setValues(asList((String[]) array));
	
			return wrapper;
		}
	
		if (array instanceof IsSerializable[])
		{
			ObjectArrayWrapper wrapper = new ObjectArrayWrapper();
			wrapper.setValues(asList((IsSerializable[]) array));
	
			return wrapper;
		}
	
		/*
		 * At this point we hope that it's an array of arrays. If not, an
		 * invalid argument exception is thrown.
		 */
		if (array instanceof Object[]) { return createArrayArrayWrapper((Object[]) array); }
	
		throw new InvalidArgumentException(array.getClass() + " is a type that cannot be serialized.");
	}

	@SuppressWarnings("unchecked")
	private static <T> ArrayArrayWrapper<T> createArrayArrayWrapper(Object[] vals) throws InvalidArgumentException
	{
		ArrayList<ArrayWrapper<?, T>> values = new ArrayList<ArrayWrapper<?, T>>(vals.length);
	
		for (Object v : vals)
			values.add((ArrayWrapper<?, T>) wrapArray(v));
	
		ArrayArrayWrapper<T> wrapper = new ArrayArrayWrapper<T>();
		wrapper.setValues(values);
	
		return wrapper;
	}
}
